package hvzt.minimap.activities;

import hvzt.minimap.R;
import hvzt.minimap.activities.mapItems.ZMapOverlay;
import hvzt.minimap.location.LocationService;
import iface.HvZUser;
import iface.UserTypes;

import java.util.Collection;
import java.util.List;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;

/**
 * An extension of MapActivity that accomodates pins that belong to the human
 * and zombie teams and changes their locations whenever the LocationService receives
 * a new list of locations.
 * @author JakeS
 *
 */
public final class ZMapActivity extends MapActivity{

	private static final double GPS_FACTOR = 1E6;

	// Roughly the CSE building at the University of Washington
	private static final int DEFAULT_LATITUDE = (int)(47.653522*GPS_FACTOR);
	private static final int DEFAULT_LONGITUDE = (int)(-122.305545*GPS_FACTOR);


	private static final int DEFAULT_ZOOM = 17;// most of campus can fit on screen
	private static final String TAG = "ZMapActivity";// for logging

	private HvZUser mSelfUser;
	//private ZMapOverlay mPins;
	private MyLocationOverlay myLoc;
	private final Messenger mThisMessenger = new Messenger(new IncomingHandler());
	private List<Overlay> mMapOverlays;
	private MapView mapView;

	private boolean mIsBound; // registered with the loc service
	private Messenger mLocationMessenger; // locService's messenger

	private MapController myMapController; // for centering the map

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// make a HvZUser to test for self so that we don't duplicate map pins
		SharedPreferences pref = getSharedPreferences("hvzt", MODE_PRIVATE);
		mSelfUser = new HvZUser(pref.getString("netId", ""), UserTypes.OBSERVER, pref.getString("telephone", ""));

		// layout the map
		setContentView(R.layout.map);
		mapView = (MapView) findViewById(R.id.mapview);
		myMapController = mapView.getController();
		myMapController.setZoom(DEFAULT_ZOOM);
		mapView.setBuiltInZoomControls(true);

		// add the current location overlay
		myLoc = new MyLocationOverlay(ZMapActivity.this, mapView);
		myLoc.enableMyLocation();
		mMapOverlays = mapView.getOverlays();
		mMapOverlays.add(myLoc);

		// scroll the map to center on the default position
		myMapController.animateTo(new GeoPoint(DEFAULT_LATITUDE, DEFAULT_LONGITUDE));

		enableTracking();
	}

	// Joseph Miau here, added code for menu
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.map_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.kill:
			startActivity(new Intent(ZMapActivity.this, KillActivity.class));
			return true;
		case R.id.toggle_service:
			if(mIsBound){
				disableTracking();
			} else {
				enableTracking();
			}			
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	private void enableTracking() {
		startService(new Intent(ZMapActivity.this, LocationService.class));
		doBindService();
		myLoc.enableMyLocation();
	}

	private void disableTracking() {
		doUnbindService();
		stopService(new Intent(ZMapActivity.this, LocationService.class));
		myLoc.disableMyLocation();
	}

	@Override
	protected void onDestroy() {
		// TODO(flp) remove this later, we shouldn't be stopping every time we finish the activity
		// TODO(jake) when does this get called? how do we want to handle people wanting to actually quit the game and stop the GPS?
		doUnbindService();
		super.onDestroy();
	}

	/**
	 * Is an overlay for this device's location displayed?
	 * Required by Google Maps TOS
	 * @return true 
	 */
	protected boolean isLocationDisplayed() {
		return true;
	}

	/**
	 * Are routes displayed?
	 * Required by Google Maps TOS
	 * @return false
	 */
	protected boolean isRouteDisplayed() {
		return false;
	}


	/**
	 * Handler of incoming messages from location service.
	 * This is what gets called by location service and given an updated
	 * list of player pins.
	 */
	class IncomingHandler extends Handler {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case LocationService.MSG_UPDATE_LOCATIONS:
				iface.Status receivedLocList = (iface.Status)msg.obj;
				Collection<HvZUser> players = receivedLocList.getLocationList();

				if(players == null) {
					Log.e(TAG, "received users null, breaking update operation");
					break;
				}

				Log.i(TAG, "received " + players.size() + " player pins");		

				ZMapOverlay zOverlays = new ZMapOverlay(getResources().getDrawable(R.drawable.zombie), ZMapActivity.this);
				ZMapOverlay hOverlays = new ZMapOverlay(getResources().getDrawable(R.drawable.human), ZMapActivity.this);				

				for(HvZUser player : players){

					if(!player.isSameUser(mSelfUser)) {
						switch(player.getUserType()){
						case ZOMBIE:
							zOverlays.addUser(player);
							break;
						case HUMAN:
							hOverlays.addUser(player);
						}
					}					
				}

				refreshOverlays(zOverlays, hOverlays);
				break;
			default:
				super.handleMessage(msg);
			}
		}

		// refresh the list of map overlays
		private void refreshOverlays(ZMapOverlay zOverlays,
				ZMapOverlay hOverlays) {
			mMapOverlays.clear();
			mMapOverlays.add(myLoc);
			mMapOverlays.add(zOverlays);
			mMapOverlays.add(hOverlays);

			mapView.invalidate();
		}
	}	

	/**
	 * Class for interacting with the main interface of the service.
	 */
	private ServiceConnection mConnection = new ServiceConnection() {
		public void onServiceConnected(ComponentName className,
				IBinder service) {
			mLocationMessenger = new Messenger(service);

			// We want to monitor the service for as long as we are
			// connected to it.
			try {
				Message msg = Message.obtain(null,
						LocationService.MSG_REGISTER_CLIENT);
				msg.replyTo = mThisMessenger;
				mLocationMessenger.send(msg);
			} catch (RemoteException e) {
				// In this case the service has crashed before we could even
				// do anything with it; we can count on soon being
				// disconnected (and then reconnected if it can be restarted)
				// so there is no need to do anything here.
			}
		}

		public void onServiceDisconnected(ComponentName className) {
			// This is called when the connection with the service has been
			// unexpectedly disconnected -- that is, its process crashed.
			mLocationMessenger = null;
		}
	};

	// starts the location service and binds this to it
	private void doBindService() {
		// Establish a connection with the service.  We use an explicit
		// class name because there is no reason to be able to let other
		// applications replace our component.
		bindService(new Intent(ZMapActivity.this, 
				LocationService.class), mConnection, Context.BIND_AUTO_CREATE);
		mIsBound = true;
	}

	// unbinds this from the LocationService
	private void doUnbindService() {
		if (mIsBound) {
			// If we have received the service, and hence registered with
			// it, then now is the time to unregister.
			if (mLocationMessenger != null) {
				try {
					Message msg = Message.obtain(null,
							LocationService.MSG_UNREGISTER_CLIENT);
					msg.replyTo = mThisMessenger;
					mLocationMessenger.send(msg);
				} catch (RemoteException e) {
					// There is nothing special we need to do if the service
					// has crashed.
				}
			}

			// Detach our existing connection.
			unbindService(mConnection);
			mIsBound = false;
		}
	}
	
	/**
	 * @return true if the ZMapActivity is currently bound to the location service 
	 * and is updating the position of the device using GPS.
	 */
	public boolean isUsingGPS(){
		return mIsBound;
	}
}
